import { ApiCall, BaseConnection } from "tsrpc";
import { SubWorldConfigItem } from "../../shared/SubWorldConfig";
import { ServiceType } from "../../shared/protocols/serviceProto_worldServer";
import { IGomokuGameData, IGomokuPlayer } from "../../shared/protocols/worldServer/gomoku/GomokuTypeDef";
import { IUserData } from "../../shared/types/SubWorldData";
import { WorldServerConn } from "../WorldServer";
import { SubWorld } from "./SubWorld";
import { ReqPlacePiece, ResPlacePiece } from "../../shared/protocols/worldServer/gomoku/PtlPlacePiece";
import { GomokuBoardMgr } from "../../shared/GomokuBoardMgr";

export class SubWorldGomoku extends SubWorld {

    private _gameData: IGomokuGameData = {
        players: [],
        currentPlayer: '',
        boardData: undefined,
    }

    private _boardMgr: GomokuBoardMgr = new GomokuBoardMgr();

    constructor(id: string, displayId: string, name: string, config: SubWorldConfigItem, password?: string) {
        super(id, name, displayId, config, password);
        this.data.maxUser = 50;
        this.data.maxPlayerNum = 2;

        this._gameData.boardData = this._boardMgr.gridData;
    }

    listenMsgs(conn: WorldServerConn) {
        super.listenMsgs(conn);
    }

    unlistenMsgs(conn: WorldServerConn) {
        super.unlistenMsgs(conn);
    }

    onUserEnter(conn: WorldServerConn) {
        conn.sendMsg("gomoku/GameDataSyncPush", { data: this._gameData });
    }

    onPlayerLeave(conn: WorldServerConn) {
        for (let i = 0; i < this._gameData.players.length; ++i) {
            let p = this._gameData.players[i];
            if (p.uid == conn.currentUser?.uid) {
                this._gameData.players.splice(i, 1);
                this.broadcastMsg("gomoku/PlayerLeavesPush", { uid: p.uid });
                if (this._gameData.players.length == 0) {
                    this._gameData.currentPlayer = '';
                    this.broadcastMsg("gomoku/GameDataChangedPush", { currentPlayer: this._gameData.currentPlayer });
                }
                break;
            }
        }
        if (this._data.isPlaying) {
            this.broadcastMsg("gomoku/GameOverPush", { winner: this._gameData.players[0].uid });
            this._data.isPlaying = false;
            this.broadcastMsg("SubWorldDataChangedPush", { isPlaying: this._data.isPlaying });
        }
    }

    onJoinGame(newUser: IUserData) {

        let newPlayer: IGomokuPlayer = {
            uid: newUser.uid,
            color: this._gameData.players.length ? 'white' : 'black',
        };

        //add to player list
        this._gameData.players.push(newPlayer);
        this.broadcastMsg("gomoku/PlayerComesPush", { player: newPlayer });

        //notify to remove from watcher list.

        //set as current player
        if (!this._gameData.currentPlayer) {
            this._gameData.currentPlayer = newPlayer.uid;
            //
            this.broadcastMsg("gomoku/GameDataChangedPush", { currentPlayer: this._gameData.currentPlayer });
        }

        return true;
    }

    onCheckGameBegin() {
        if (this.data.playerNum < this.data.maxPlayerNum) {
            return;
        }

        let readyCnt = 0;
        this._gameData.players.forEach(v => {
            if (this._userMap.get(v.uid)?.ready) {
                readyCnt++;
            }
        });

        if (readyCnt == this.data.maxPlayerNum) {
            this.onResetGameData();
            this._data.isPlaying = true;
            this.broadcastMsg("SubWorldDataChangedPush", { isPlaying: this._data.isPlaying });

            let p = this._gameData.players[Math.floor(Math.random() * 2)];
            this._gameData.currentPlayer = p.uid;


            this.broadcastMsg("gomoku/GameDataChangedPush", { currentPlayer: this._gameData.currentPlayer, boardData: this._gameData.boardData });
            this.broadcastMsg("gomoku/GameBeginPush", {});
        }
    }

    onResetGameData() {
        this._gameData.currentPlayer = '';
        this._data.isPlaying = false;
        this._boardMgr.reset();
    }

    /***
 * @en get player by connnection, null means not a player.
 * @zh 通过链接查找对应的玩家, null 表示不是玩家
 */
    getPlayer(uid: string) {
        for (let i = 0; i < this._gameData.players.length; ++i) {
            let p = this._gameData.players[i];
            if (p.uid == uid) {
                return p;
            }
        }

        return null;
    }

    gameOver(winner: string) {
        this.data.isPlaying = false;
        for (let i = 0; i < this._gameData.players.length; ++i) {
            let p = this._gameData.players[i];
            this._userMap.get(p.uid)!.ready = false;
        }
        this.broadcastMsg("SubWorldDataChangedPush", { isPlaying: this._data.isPlaying });
        this.broadcastMsg("gomoku/GameOverPush", { winner: winner });
    }

    onRPC_PlacePiece(call: ApiCall<ReqPlacePiece, ResPlacePiece>) {
        const conn = call.conn as WorldServerConn;

        let currentUser = conn.currentUser;

        if (!currentUser) {
            return call.error('INVALID_USER');
        }

        let user = this._userMap.get(currentUser.uid);
        if (!user) {
            return call.error('INVALID_USER_DATA');
        }

        if (this._gameData.currentPlayer != user.uid) {
            return;
        }

        let req = call.req;

        let sg = this._boardMgr.sizeInGrid;
        if (req.gridX < 0 || req.gridX >= sg || req.gridY < 0 || req.gridY >= sg) {
            return call.error('INVALID_GRID');
        }

        let v = this._boardMgr.getGridData(req.gridX, req.gridY);
        if (v) {
            return call.error('INVALID_GRID');
        }

        let p = this.getPlayer(user.uid);
        v = p?.color == 'black' ? 1 : 2;

        this._boardMgr.setGrid(req.gridX, req.gridY, v);

        call.succ({});

        this.broadcastMsg("gomoku/PlacePiecePush", { uid: user.uid, gridX: req.gridX, gridY: req.gridY, value: v });

        if (this._data.isPlaying) {
            //
            let win = this._boardMgr.checkWin(req.gridX, req.gridY, v);
            if (win) {
                this.gameOver(this._gameData.currentPlayer);
            }
            else {
                for (let i = 0; i < this._gameData.players.length; ++i) {
                    let p = this._gameData.players[i];
                    if (this._gameData.currentPlayer == p.uid) {
                        let nextTurn = (i + 1) % this._gameData.players.length;
                        this._gameData.currentPlayer = this._gameData.players[nextTurn].uid;
                        break;
                    }
                }
                this.broadcastMsg("gomoku/GameDataChangedPush", { currentPlayer: this._gameData.currentPlayer });
            }
        }
    }
}